/**
 * create by young (young2f@outlook.com) on 2020/12/3
 */
'use strict';
const base = require('../base/serviceBase');
const _ = require('lodash');
const _stringUtils = require('../utils/stringUtils');
module.exports = class extends base {
    async createNode(node) {
        const {ctx} = this;
        /**
         * 没有父节点
         */
        if (!node.fatherId) {
            node.path = '/';
            node.level = 0;
            node.rootName = node.name;
            const result = await ctx.model.Tree(node)
                .save();
            return {status: 'ok', result};
        }
        /**
         * 有父节点
         */
        const {fatherId} = node;
        const fatherNode = await ctx.app.dbs.findOne('Tree', {treeId: fatherId});
        if (!fatherNode) {
            throw new Error('父节点不存在！');
        }

        let {level, path, name,rootName, title} = fatherNode;
        const {label} = title;
        node.rootName = rootName;
        node.level = level + 1;
        node.path = `${path}${label} ${name} /`;
        node.fatherTitle = label;
        node.fatherName = name;

        const result = await ctx.app.dbs.insert('Tree', node);

        return {status: 'ok', result};
    }

    async createNodeBatch(body) {
        const {ctx} = this;
        if (!body || !body.length) {
            return {status: 'ok'};
        }

        try {
            const model = {
                title: {type: 'object'},
                createUser: {type: 'string'},
                table: {type: 'string'},
                tenantCode: {type: 'string'},
            };
            for (let node of body) {
                ctx.validate(model, node);
                node.treeId = _stringUtils.getCode('TRE');
                const result = await this.createNode(node);
            }
            // const result = await ctx.model.Tree.insertMany(body);
            return {status: 'ok', result: 'ok'};

        } catch (e) {
            console.error(e);
            const _message = e && e.message ? e.message : this.ERROR_MSG;
            return {status: 'error', message: _message, result: e};
        }
    }

    async findTree(body) {
        const {ctx} = this;
        if (!body.hasOwnProperty('table')) {
            throw new Error('缺少查询对象:table');
        }
        const {table} = body;
        const result = Array.isArray(table) ? await this.findTrees(body) : await this._findTree(body);
        return result;
    }

    async findTrees(body) {
        const {table, tenantCode} = body;
        const res = [];
        for (let t of table) {
            const where = {table: t, tenantCode};
            const temp = await this._findTree(where);
            const {result} = temp;
            result && res.push(...result);
        }
        return {status: 'ok', result: res};
    }

    async _findTree(where) {
        const {ctx} = this;
        /**
         * 所有节点
         */
        const allNode = await ctx.app.dbs.find('Tree', where);

        /**
         * 过滤出根节点
         * @type {Array}
         */
        const rootNodes = _.filter(allNode, item => {
            return item.level === 0;
        });

        /**
         * 获取子节点
         * @type {Array}
         */
        const result = [];
        rootNodes && rootNodes.length && rootNodes.forEach(item => {
            getChildren(item, allNode);
            result.push(item);
        });
        return {status: 'ok', result};
    }

    async findData(where) {
        const {ctx} = this;
        const {table} = where;

        const model = this.getModelStr(table);
        delete where['table'];
        const result = ctx.model.hasOwnProperty(model) ? await ctx.app.dbs.find(model, where) : [];
        return {status: 'ok', result};
    }

    async findNodes(body) {
        const {ctx} = this;
        if (!body.hasOwnProperty('table')) {
            throw new Error('缺少查询对象:table');
        }
        const {table, tenantCode} = body;
        const where = Array.isArray(table) ? getTables(table, tenantCode) : body;
        const result = await ctx.app.dbs.find('Tree', where);
        return {status: 'ok', result};
    }

    async moveNode(body) {
        const {ctx} = this;
        const {table, source, dist, tenantCode} = body;
        /**
         * 源节点
         * @type {{_id: *, table: *}}
         */

        let sourceNode = await ctx.app.dbs.findOne('Tree', {table, treeId: source, tenantCode});

        /**
         * 目标节点
         */

        const distNode = await ctx.app.dbs.findOne('Tree', {table, treeId: dist, tenantCode});

        /**
         * 要移动的日录就在目标目录下
         */
        if (sourceNode.fatherId === dist) {
            throw new Error('源日录就在目标目录下！');
        }

        const allNode = await ctx.app.dbs.find('Tree', {tenantCode, table});

        /**
         * 要移动的日录是目标目录的上级
         * 上级目录不可以移到下级目录下，会嵌套
         */

        const distPath = [];
        getNodePath(distPath, distNode, allNode);

        if (_.includes(distPath, source)) {
            throw new Error('目标目录属于源目录的子集！');
        }

        let label = distNode.title && distNode.title.label ? distNode.title.label : '';
        sourceNode.fatherTitle = label;
        let name = distNode.name ? distNode.name : '';
        sourceNode.fatherName = name;
        /**
         * 移动不是复制
         */

        sourceNode.fatherId = dist;
        sourceNode.level = distNode.level + 1;
        sourceNode.path = distNode.path + distNode.name + '/';

        const {treeId} = sourceNode;
        const where = {table, treeId};

        const result = await ctx.app.dbs.updateOne('Tree', where, sourceNode);

        return {status: 'ok', result};
    }

    async copyNode(body) {
        const {ctx} = this;
        const {table, source, dist, tenantCode} = body;
        /**
         * 源节点
         * @type {{_id: *, table: *}}
         */

        const sourceNode = await ctx.app.dbs.findOne('Tree', {table, treeId: source, tenantCode});

        /**
         * 目标节点
         */

        const distNode = await ctx.app.dbs.findOne('Tree', {table, treeId: dist, tenantCode});

        /**
         * 要移动的日录就在目标目录下
         */
        if (sourceNode.fatherId === dist) {
            throw new Error('源日录就在目标目录下！');
        }

        const allNode = await ctx.app.dbs.find('Tree',{tenantCode, table});

        /**
         * 要移动的日录是目标目录的上级
         * 上级目录不可以移到下级目录下，会嵌套
         */

        const distPath = [];
        getNodePath(distPath, distNode, allNode);
        if (_.includes(distPath, source)) {
            throw new Error('目标目录属于源目录的子集！');
        }

        let label = distNode.title && distNode.title.label ? distNode.title.label : '';
        sourceNode.fatherTitle = label;
        let name = distNode.name ? distNode.name : '';
        sourceNode.fatherName = name;
        delete sourceNode['_id'];
        const list = [];
        /**
         * 获取源节点和节点下的所有子节点
         */
        sourceNode.fatherId = distNode.treeId;
        getNodes(list, sourceNode, allNode);
        /**
         * 新建节点的_id，并更新子节点的fatherId
         */
            // list.forEach(item => {
            //   const { treeId } = item;
            //   if (treeId === sourceNode.treeId) {
            //     const { treeId, path, level } = distNode;
            //     let label = distNode.title && distNode.title.label ? distNode.title.label : '';
            //     item.fatherTitle = label;
            //     let name = distNode.name ? distNode.name : '';
            //     item.fatherName = name;
            //     item.fatherId = treeId;
            //     item.editable = true;
            //     item.path = `${path}${label} ${name} /`;
            //     item.level = level + 1;
            //   }
            //   const newId = _stringUtils.getCode('TRE');
            //   item.treeId = newId;
            //   list.forEach(item => {
            //     if (item.fatherId === treeId) {
            //       item.fatherId = newId;
            //     }
            //   });
            // });

        let count = 0;
        list.map(async item => {
            const result = await ctx.model.Tree(item)
                .save();
            count++;
        });
// const result = await ctx.model.Tree.insertMany(body);
        return {status: 'ok', result: 'ok', count};
    }

    async moveTreeData(body) {
        const {ctx} = this;

        for (let item of body) {
            const {treeId, table, code, distId} = item;
            delete item['distId'];
            delete item['_id'];
            delete item['table'];
            item.treeId = distId;
            // const prefix = code.slice(0, 3);
            // item.code = _stringUtils.getCode(prefix);
            const model = this.getModelStr(table);
            const res = await ctx.model[model](item)
                .save();

            const res1 = await ctx.app.dbs.deleteOne(model,{treeId, code});
        }

        return {status: 'ok'};
    }
};

function getChildren(node, allNode) {
    const {treeId} = node;
    let childrenList = allNode.filter(item => {
        const {fatherId} = item;
        return fatherId === treeId;
    });

    node.children = childrenList;
    if (!childrenList) {
        return;
    }
    return childrenList.forEach(item => {
        getChildren(item, allNode);
    });
}

/**
 * 递归获取目录节点的路径
 * @param node
 * @param allNode
 */
function getNodePath(path, node, allNode) {
    const {level, fatherId} = node;
    if (level === 0) {
        return;
    }
    const temp = allNode.filter(item => {
        return item.treeId === fatherId;
    });
    if (temp && temp.length) {
        const _node = temp[0];
        const {treeId} = _node;
        path.push(treeId);
        getNodePath(path, _node, allNode);
    }
}

/**
 * 递归获取节点，包括节点下的子节点
 * 把嵌套的子节点拉平
 * @param list：返回的节点列表
 * @param node：需要处理的节点，包括有children
 * @param createUser
 * @param table
 */
function getNodes(list, node, allNode) {
    const {treeId} = node;
    node.treeId = _stringUtils.getCode('TRE');
    delete node['_id'];
    list.push(node);
    const children = _.filter(allNode, item => item.fatherId === treeId);
    if (children && children.length) {
        children.forEach(item => {
            item.fatherId = node.treeId;
            getNodes(list, item, allNode);
        });
    }
    // const { children } = node;
    // const _node = _.cloneDeep(node);
    // delete _node['children'];
    // delete _node['_id'];
    //
    // if (children && children.length) {
    //   list.push(_node);
    //   children.forEach(item => {
    //     getNodes(list, item, allNode);
    //   });
    //   return;
    // }
    // list.push(_node);
}

function getTables(tables, tenantCode) {
    const temp = [];
    for (let table of tables) {
        temp.push({table, tenantCode});
    }
    const where = {'$or': temp};
    return where;
}
